package common;

import java.io.Serializable;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;

// This class is implemented as an atomic class
public class VectorClock implements Serializable {

	private static final long serialVersionUID = 1L;
	private ClockComparator cc = new ClockComparator();
	private ArrayList<Clock> clocks = new ArrayList<Clock>();
	private boolean initialized = false;

	public synchronized void addClock(URI node, Integer clock) {
		try {

			// Add only if not already present
			if (!clocks.contains(new Clock(node, 0)))
				clocks.add(new Clock(new URI(node.toString()), new Integer(
						clock)));
		} catch (URISyntaxException e) {

			// This should never happen because a new URI is constructed from
			// the String obtained invoking URI.toString().
			e.printStackTrace();
		}
		Collections.sort(clocks, cc);
	}

	public synchronized int clocksNumber() {
		return clocks.size();
	}

	public synchronized int clockValue(int index) {
		return clocks.get(index).clock;
	}

	public synchronized boolean comparable(VectorClock otherVectorClock) {

		// Check that the vector clocks are comparable (same size and same
		// nodes in the same sorted order)
		if (this.clocks.size() != otherVectorClock.clocks.size())
			return false;
		int size = this.clocks.size();
		for (int i = 0; i < size; i++)
			if (!this.clocks.get(i).equals(otherVectorClock.clocks.get(i)))
				return false;
		return true;
	}

	public synchronized VectorClock getCopy() {
		VectorClock copy = new VectorClock();
		copy.cc = cc;
		copy.initialized = initialized;
		for (Clock c : clocks)
			copy.clocks.add(new Clock(c.node, c.clock));
		return copy;
	}

	public synchronized void incrementClock(URI node) {
		for (Clock c : clocks)
			if (c.node.equals(node))
				c.clock++;
	}

	public synchronized boolean isInitialized() {
		return initialized;
	}

	public synchronized void mergeWith(VectorClock other) {
		for (int i = 0; i < this.clocks.size(); i++)
			this.clocks.get(i).clock = this.clocks.get(i).clock > other.clocks
					.get(i).clock ? this.clocks.get(i).clock : other.clocks
					.get(i).clock;
	}

	public synchronized boolean removeClock(URI node) {
		return clocks.remove(new Clock(node, 0));
	}

	public synchronized void setInitialized(boolean initialized) {
		this.initialized = initialized;
	}

	@Override
	public synchronized String toString() {
		String str = "";
		if (clocks.size() == 0)
			return "";
		else
			str += ("[ " + clocks.get(0).node.toString() + ":" + clocks.get(0).clock);
		for (int i = 1; i < clocks.size(); i++) {
			str += (" - " + clocks.get(i).node.toString() + ":" + clocks.get(i).clock);
		}
		str += " ]";
		return str;
	}

}
